#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/printk.h>
#include <linux/kern_levels.h>
#define GLOBAMEM_SIZE 0x1000
#define GLOBAMEM_MAJOR 230
//#define GLOBAMEM_MAJOR 0
#define MEM_CLEN_CMD    0x01
#define DEVICE_NUM      10
static int globalmem_major = GLOBAMEM_MAJOR;
/**
 * #define S_IRUGO		(S_IRUSR|S_IRGRP|S_IROTH)
 * #define S_IRUSR 00400  user read
 * #define S_IRGRP 00040  user grep read
 * #define S_IROTH 00004  oter  read 
 * S_IRUGO = 0x00444
*/
//module_parem(globamem_major,int,S_IRUGO);  


struct globalmem_dev{
    struct cdev cdev;
    unsigned char mem[GLOBAMEM_SIZE];
    struct mutex m_mutex;
};

struct globalmem_dev* globalmem_devp;
/**
 * 用来修改一个文件当前读写位置
 * 并将新的位置返回
 * 在出错的时候返回一个负值
 * 0 set now offset
 * 1 add now offset
 * -EINVAL 无效参数
*/
static loff_t globalmem_llseek (struct file * file, loff_t  offset, int orig)
{
    loff_t ret =0;
    switch (orig)
    {
    case 0:
        if((offset < 0) || (offset > GLOBAMEM_SIZE))
        ret = -EINVAL;
         file->f_pos = (unsigned int)offset;
        break;
    case 1:
         if((file->f_pos + offset < 0) || (file->f_pos+offset > GLOBAMEM_SIZE))
        ret = -EINVAL;
         file->f_pos = (unsigned int)offset;
        break;
    default:
        ret = -EINVAL;
        break;
    }
    return ret;
}
/**
 * 从设备中读取数据  返回0 end-of-file(EOF) 
 * EFAULT   ----   It happen if the memory address of some argument 
 * passed to sendto (or more generally to any system call) 
 * is invalid. Think of it as a sort of SIGSEGV in kernel 
 * land regarding your syscall. For instance, if you pass 
 * a null or invalid buffer pointer (for reading, writing, sending, recieving...)。
 * 说明在对套接字上调用某些函数的时候传入了空指针等非法參数，的确如此。
*/
static ssize_t globalmem_read (struct file * file, char __user * buff, size_t size, loff_t *offset)
{
    unsigned int p = *offset;
    unsigned int count = size;
    ssize_t ret=0;
    struct globalmem_dev *dev = file->private_data;

    if(*offset > GLOBAMEM_SIZE)
    return 0;
     /**
      * count 合法性判断
      * 如果 当前需要读出数据大于了 当前已有大小则全部读出
     */
    if(count > GLOBAMEM_SIZE - p)
        count = GLOBAMEM_SIZE - p;
    mutex_lock(&dev->m_mutex);//进入互斥体
    if(copy_to_user(buff,dev->mem+p,count)){
        ret  = -EFAULT;
    }else {
        *offset = p+count;
        ret = count;
         printk("read %u bytes(s) from %x\n", count, p);
    }    
    mutex_unlock(&dev->m_mutex);//退出互斥体
    return ret;
}
/**
 * 写数据到设备中  返回0 end-of-file(EOF) 
*/
static ssize_t globalmem_write (struct file *file, const char __user *buff, size_t size, loff_t *offset)
{
    unsigned int p = *offset;
    unsigned int count = size;
    ssize_t ret=0;
    struct globalmem_dev *dev = file->private_data;

    if(*offset > GLOBAMEM_SIZE)
    return 0;
     /**
      * count 合法性判断
      * 如果 当前需要读出数据大于了 当前已有大小则全部读出
     */
    if(count > GLOBAMEM_SIZE - p)
        count = GLOBAMEM_SIZE - p;
    mutex_lock(&dev->m_mutex);
    if(copy_from_user(dev->mem+p,buff,count)){
        ret  = -EFAULT;
    }else {
        *offset = p+count;
        ret = count;
         printk("write %u bytes(s) from %x\n", count, p);
    }  
    mutex_unlock(&dev->m_mutex);  
    return ret;
}
/**
 * 提供设备相关的控制命令  对应应用程序中的  int fcntl(int fd,int cmd,...)
*/
long globalmem_unlocked_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
    struct globalmem_dev *dev = file->private_data;
    switch (cmd)
    {
    case MEM_CLEN_CMD:
        mutex_lock(&dev->m_mutex);
        memset(dev->mem,0,GLOBAMEM_SIZE);
        mutex_unlock(&dev->m_mutex);
        printk("globalmem set 0 form mem \n");
        break;
    
    default:
        return -EINVAL;
        break;
    }
    return 0;
}
/**
 * 对应应用程序的 open函数
 * 设置 node 节点的私有数据
*/
int globalmem_open (struct inode *node, struct file *file)
{

    //container_of 通过结构体成员变量的地址，获取这个结构体的地址
    //这个地方的 node->i_cdev 也就是 globalmem_dev 中的cdev
    /*container_of需要传入三个参数，第一个参数是一个指针，第二个参数是结构体类型，
                第三个是对应第二个参数里面的结构体里面的成员。

        container_of(ptr, type, member)

        ptr:表示结构体中member的地址 h
        type:表示结构体类型 struct sensor_private_data
        member:表示结构体中的成员 early_suspend type里面一定要有这个成员，
        返回结构体的首地址
    */
    struct globalmem_dev *dev = container_of(node->i_cdev,
                                            struct globalmem_dev,
                                            cdev);
    file->private_data = dev;
     printk("%s set globalmem private data \n",__func__);
    return 0;
}
/**
 * 释放时调用
*/
int globalmem_release (struct inode *node, struct file *file)
{
     printk("%s this function didn't anyting \n",__func__);
    return 0;
}
static const struct file_operations globalmem_fops = {
    .owner = THIS_MODULE,
    .llseek = globalmem_llseek, //用来修改文件的当前读写位置
    .read = globalmem_read,    
    .write = globalmem_write,
    .unlocked_ioctl = globalmem_unlocked_ioctl, //提供设备相关的控制命令  用户调用 int fcnctl 函数时调用
    .open = globalmem_open,
    .release = globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index){
    int err;
    /**
     * #define MKDEV(ma,mi)	((ma)<<8 | (mi))
    */
    int dev_node = MKDEV( globalmem_major,index);
    /**
     * void cdev_init(struct cdev *cdev, const struct file_operations *fops)
    */
    cdev_init(&dev->cdev,&globalmem_fops);
    dev->cdev.owner = THIS_MODULE;
    /**
     * int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    */
    err = cdev_add(&dev->cdev,dev_node,1);
}
static int __init  globalmem_init(void){
    int ret ;
    int i;
    dev_t dev_node = MKDEV(globalmem_major,0);
    if(globalmem_major){
        /**
         * int register_chrdev_region(dev_t from,unsigned count,const char* name) 
         * 用于已知设备的主设备号的情况下 注册字符设备驱动
        */
        ret =register_chrdev_region(dev_node,DEVICE_NUM,"globalmem");    
    }else {
        /**
         * int alloc_chrdev_region(dev_t form,unsigned daseminor,unsigned cont,const char* name)
         * 向系统动态申请未被占用的设备号
        */
        ret = alloc_chrdev_region(&dev_node,0,DEVICE_NUM,"globalmem");
        globalmem_major = MAJOR(dev_node);
    }
    
    if(ret < 0)
     return ret;
    /**
     * #define GFP_KERNEL(__GFP_WAIT | __GFP_IO | __GFP_FS)
        __GFP_WAIT ： 缺内存页的时候可以睡眠;
        __GFP_IO ： 允许启动磁盘IO；
        __GFP_FS ： 允许启动文件系统IO。
    */
    globalmem_devp = kzalloc(sizeof(struct globalmem_dev)*DEVICE_NUM,GFP_KERNEL);
    if(!globalmem_devp)
    {
        ret = -ENOMEM;
        goto fail_malloc;
    }
    mutex_init(&globalmem_devp->m_mutex);
    for(i=0;i<DEVICE_NUM;i++)
    globalmem_setup_cdev(globalmem_devp+i,i);//设置字符驱动
    printk("%s init\n",__func__);
    return 0;
fail_malloc:
    unregister_chrdev_region(dev_node,1);
    return ret;
}

module_init(globalmem_init);
static void __exit globalmem_exit(void){
    int i;
    for(i =0;i<DEVICE_NUM;i++)
        cdev_del(&(globalmem_devp+i)->cdev);
    kfree(globalmem_devp);
    unregister_chrdev_region(MKDEV(globalmem_major,0),DEVICE_NUM);    
    printk("%s exit\n",__func__);
}
module_exit(globalmem_exit);

MODULE_AUTHOR("302501638@qq.com");
MODULE_LICENSE("GPL v2");
